<?php defined('SYSPATH') or die('No direct script access.');
/**
 * 数据库迭代器
 *
 * @author  Yellow.Chow <aultoale@gmail.com>
 * @version $Id: iterator.php 119 2012-03-29 10:22:26Z yellow.chow $
 * @license http://www.gnu.org/licenses/gpl-3.0.html    GPL 3
 */
class Database_Iterator implements SeekableIterator, Countable
{

    /**
     * select 查询语句
     *
     * @var Database_Query_Builder_Select
     */
    protected $select = NULL;

    /**
     * 数据库实例名
     *
     * @var string
     */
    protected $db = NULL;

    /**
     * 迭代步长
     *
     * @var int
     */
    protected $step = 20;

    /**
     * 总页数
     *
     * @var int
     */
    protected $count = NULL;

    /**
     * 总记录数
     *
     * @var int
     */
    protected $total = NULL;

    /**
     * 数据指针
     *
     * @var int
     */
    protected $position = 0;

    /**
     * 构造方法
     *
     * @param  Database_Query_Builder_Select  $select
     * @param  string  $db
     * @param  int     $step
     */
    public function __construct(Database_Query_Builder_Select $select, $db = NULL, $step = NULL)
    {
        $this->select = $select;

        $this->db = $db;

        if ( ! is_null($step))
        {
            $this->step($step);
        }
    }

    /**
     * 设置迭代步长
     *
     * @param  int  $step
     * @throws Exception
     */
    public function step($step)
    {
        if ( ! (is_numeric($step) AND $step > 0))
        {
            throw new Exception('数据库迭代器 (DB_Iterator) 的步长必须为数字且大于 0');
        }

        // 重置游标与数据
        if ($step !== $this->step)
        {
            $this->position = 0;
        }

        $this->step = $step;
    }

    /**
     * 获取当前的数据迭代器
     *
     * @return array
     */
    public function current()
    {
        return $this->select->limit($this->step)
            ->offset($this->position * $this->step)
            ->execute($this->db)
            ->as_array();
    }

    /**
     * 将指针下移，并返回数据
     *
     * @throws Exception
     */
    public function next()
    {
        if ($this->position >= $this->count())
        {
            throw new Exception('当前指针已指向记录最底部');
        }

        $this->position++;
    }

    /**
     * 返回当前指针
     *
     * @return int
     */
    public function key()
    {
        return $this->position;
    }

    /**
     * 重置内部指针
     */
    public function rewind()
    {
        $this->position = 0;
    }

    /**
     * 返回当前位置是否有数据
     *
     * @return bool
     */
    public function valid()
    {
        return ($this->position < $this->count());
    }

    /**
     * 定位指针位置
     *
     * @param  int   $position
     * @throws Exception
     */
    public function seek($position)
    {
        if ($position >= $this->count() OR $position < 0)
        {
            throw new Exception('无效的指针位置');
        }

        $this->position = $position;
    }

    /**
     * 实现 Countable 接口，获取迭代总步长 (总页数)
     *
     * @return int
     */
    public function count()
    {
        if ($this->count === NULL)
        {
            $this->count = ceil($this->total() / $this->step);
        }

        return $this->count;
    }

    /**
     * 统计记录总数
     *
     * @reutrn int
     */
    public function total()
    {
        if ($this->total === NULL)
        {
            $this->total = $this->select->count_all($this->db);
        }

        return $this->total;
    }

}